package cn.tedu._07springaop.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * Aspect注解:
 *   1.表示当前类为一个切面类;
 *   2.当调用目标方法[add、sub、mul、div]时,框架会先检查是否定义了切面类;
 *   3.当切面类中定义了切面方法,框架会检查目标方法[add、sub、mul、div]是否被指定了切点;
 */
@Aspect
@Component
public class LogAspect {

    /**
     * Pointcut注解: 定义切点表达式的方法,表示哪些方法需要被植入增强逻辑;
     */
    //1.拦截指定类的所有方法;
    @Pointcut(value = "execution(public int cn.tedu._07springaop.aop.CalculatorImpl.*(..))")
    //2.拦截所有类的所有方法;
    //@Pointcut(value = "execution(* * ..*.*(..))")
    //3.拦截指定类的1个方法;
    //@Pointcut(value = "execution(public int cn.tedu._07springaop.aop.CalculatorImpl.add(..))")
    //4.拦截指定类的多个方法,使用或 ||;
    //@Pointcut(value = "execution(public int cn.tedu._07springaop.aop.CalculatorImpl.add(..)) || execution(public int cn.tedu._07springaop.aop.CalculatorImpl.sub(..))")
    //5.粗粒度:bean("") IoC容器中Bean对象的名称;
    //@Pointcut(value = "bean(calculatorImpl)")
    //6.粗粒度:with("包名.类名")
    //@Pointcut(value = "within(cn.tedu._07springaop.aop.CalculatorImpl)")
    public void doTime(){}


    /**
     * Before注解: 前置通知,调用目标方法之前执行的方法;
     * value参数: 切入点表达式,表示哪些方法需要被植入增强逻辑;
     */
    @Before(value = "doTime()")
    public void beforeMethod(JoinPoint joinPoint){
        String name = joinPoint.getSignature().getName(); // 获取方法名称
        String args = Arrays.toString(joinPoint.getArgs()); // 获取方法参数

        System.out.println("[前置]" + name + "方法开始,参数为:" + args);
    }

    /**
     * After注解: 后置通知
     *   1.在目标方法彻底结束[正常结束|异常结束]之后执行;
     *   2.后置通知没有权限获取到目标方法的返回值;
     */
    @After(value = "doTime()")
    public void afterMethod(JoinPoint joinPoint){
        String name = joinPoint.getSignature().getName();
        System.out.println("[后置]" + name + "方法结束");
    }

    /**
     * AfterReturning注解: 返回通知
     *   1.在目标方法正常结束之后执行;
     *   2.返回通知有权限获取到目标方法的返回值;
     */
    @AfterReturning(value = "doTime()", returning = "result")
    public void afterReturningMethod(JoinPoint joinPoint, Object result){
        String name = joinPoint.getSignature().getName();
        System.out.println("[返回]" + name + "方法正常结束,返回值为:" + result);
    }

    /**
     * AfterThrowing注解: 异常通知
     *   1.只在目标方法抛出异常之后执行;
     *   2.异常通知有权限获取到目标方法抛出的异常对象;
     */
    @AfterThrowing(value = "doTime()", throwing = "ex")
    public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex){
        String name = joinPoint.getSignature().getName();
        System.out.println("[异常]" + name + "方法抛出异常,异常信息为:" + ex.getMessage());
    }

    /**
     * Around注解: 环绕通知
     *   1.等价于 @Before + @After + @AfterReturning + @AfterThrowing 的组合;
     *   2.环绕通知有权限访问目标方法的返回值;
     *   3.环绕通知有权限获取异常对象[目标方法抛出异常时];
     *   4.通常使用 try ... catch ... finally ...结果包裹;
     */
    @Around(value = "doTime()")
    public Object aroundMethod(ProceedingJoinPoint joinPoint){
        Object result = null;

        try {
            System.out.println("[环绕通知-前置通知]");
            result = joinPoint.proceed();
            System.out.println("[环绕通知-返回通知]");
        } catch (Throwable e) {
            System.out.println("[环绕通知-异常通知]");
        } finally {
            System.out.println("[环绕通知-后置通知]");
        }

        return result;
    }
}













